package com.idea.relax.tool.core.tree;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author azhao
 */
public class ListToTree<T> extends ListTree<T> {
    private Logger logger = LoggerFactory.getLogger(ListToTree.class);

    private List<T> transfer(List<T> source, Comparator<T> comparator) {
        List<T> tree = new ArrayList<>();
        if (check(source)) {
            logger.info("检查失败");
            return source;
        }
        init(source);
        try {
            for (T t : source) {
                T parent = (T) parentId.get(t);
                if (parent == null) {
                    T obj = cloneObject(t);
                    id.set(obj, id.get(t));
                    if (level != null) {
                        level.set(obj, 1);
                    }
                    children.set(obj, transfer(source, id.get(t), levelNumber, comparator));
                    tree.add(obj);
                }
            }
            if (comparator != null) {
                tree.sort(comparator);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return tree;
    }

    public List<T> transfer(TreeParam<T> treeParam) {
        List<T> source = treeParam.getSource();
        if (source == null || source.isEmpty()) {
            return new ArrayList<>();
        }
        ErgodicStrategy ergodicStrategy = treeParam.getErgodicStrategy();
        if (ergodicStrategy == null || ErgodicStrategy.ROOT_PID_IS_NULL.equals(ergodicStrategy)) {
            return transfer(treeParam.getSource(), treeParam.getComparator());
        } else if (ergodicStrategy == ErgodicStrategy.ROOT_ID_EQ_ROOT_PID) {
            Map<Object, Object> map = new HashMap<>();
            init(source);
            source.forEach(t -> {
                try {
                    T parent = (T) parentId.get(t);
                    T current = (T) id.get(t);
                    if (current.equals(parent)) {
                        map.put(current, parent);
                        parentId.set(t, null);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            return recovery(source, treeParam.getComparator(), map);
        } else if (ergodicStrategy == ErgodicStrategy.ROOT_PID_IS_NOT_NULL) {
            Map<Object, Object> map = new HashMap<>();
            init(source);
            Set<Object> ids = new HashSet<>();
            for (T t : source) {
                try {
                    T current = (T) id.get(t);
                    ids.add(current);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            source.forEach(t -> {
                try {
                    T parent = (T) parentId.get(t);
                    T current = (T) id.get(t);
                    if (!ids.contains(parent)) {
                        map.put(current, parent);
                        parentId.set(t, null);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            return recovery(source, treeParam.getComparator(), map);
        }
        return new ArrayList<>();
    }

    private List<T> recovery(List<T> source, Comparator<T> comparator, Map<Object, Object> map) {
        List<T> target = transfer(source, comparator);
        target.forEach(t -> {
            try {
                T current = (T) id.get(t);
                T parent = (T) map.get(current);
                if (parent != null) {
                    parentId.set(t, parent);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return target;
    }

    private List<T> transfer(List<T> source, Object o, int levelNumber, Comparator<T> comparator) throws IllegalAccessException {
        List<T> childrenList = new ArrayList<>();
        ++levelNumber;
        for (T t : source) {
            Object parentObj = parentId.get(t);
            if (parentObj != null && parentObj.equals(o)) {
                T obj = cloneObject(t);
                id.set(obj, id.get(t));
                parentId.set(obj, parentObj);
                if (level != null) {
                    level.set(obj, levelNumber);
                }
                List<T> ts = transfer(source, id.get(t), levelNumber, comparator);
                if (comparator != null) {
                    ts.sort(comparator);
                }
                children.set(obj, ts);
                childrenList.add(obj);
            }
        }
        return childrenList;
    }
}
